/*-----------------------------------------
// Declaration of the Header files required.
------------------------------------------*/



#include "syscall.h"


/*----------------------------------------------------------
// Declare global variables and Data Structures Below
----------------------------------------------------------*/

#define MAX_NO_OF_CLERKS 5
#define MAX_NO_OF_CASHREGISTERS 5
#define MAX_NO_OF_CUSTOMERS 20
#define MAX_NO_OF_AISLE 10
#define MAX_NO_SB 10
#define FALSE 0
#define TRUE 1


/* status values for cRSt
 * enum cRStEnum{NOT_SET, CLERK_WAITING, CASHREG_WAITING }; */
#define	NOT_SET 			0
#define CLERK_WAITING		1
#define CASHREG_WAITING		2

/*status values for clerstatus
 * enum ckStEnum{CLERK_FREE, CLERK_BUSY, CLERK_ONBREAK, CLERK_COMING_FROM_BREAK}; */
#define CLERK_FREE					0
#define CLERK_BUSY					1
#define CLERK_ONBREAK				2
#define CLERK_COMING_FROM_BREAK		3


#define MAX_NAME_SIZE   	30
/*buffer to hold the names of lock and CV's and MV's*/
char name[MAX_NAME_SIZE];

/*Customer data sturcture*/
typedef struct customer
{
	int noOfItemsToBePurchased;
	int itemsToBePurchased[10];
	int customerReturning;
	/*customer()
	{
		noOfItemsToBePurchased=(int)((GetRand()%10)+1);
		for(int i=0;i<noOfItemsToBePurchased;i++)
		{
			itemsToBePurchased[i]=(int)(GetRand()%10);
		}
		if(GetRand()%5==4){
			customerReturning=true;
		}
		else{
			customerReturning=false;
		}
	}*/

}customerDataTemplate;




/* cashregister data-structure*/

/*typedef struct cashregister
{

	/* only clrkLock[] should access this data member
	int customerAmount;
	/* only mCshRgLk should access this data member
	int totalAmount;
	/*totalsales at this cash counter
	int totalSalesAtCashRegister;

	cashregister(){

		customerAmount = 0;
		totalAmount = 0;
		totalSalesAtCashRegister = 0;
	}
}cashRegisterTemplate;*/

int crCuA[MAX_NO_OF_CASHREGISTERS];
int crTtl[MAX_NO_OF_CASHREGISTERS];
int crTSl[MAX_NO_OF_CASHREGISTERS];


int cuTCtr;
int cRTCtr;
int ckTCtr;
int sbTCtr;

int cusTCtrLk;
int cRgTCtrLk;
int clkTCtrLk;
int sbTCtrLk;

int testcase;
int noOfClerks;		/* values will change dynamically*/
int noOfCashReg;
int noOfCustomers;
int noOfStockBoys;
int noOfAisle;
/*cashRegisterTemplate cashRegister[MAX_NO_OF_CASHREGISTERS];*/
customerDataTemplate customerData[MAX_NO_OF_CUSTOMERS];



int itemsInStoreInfo[MAX_NO_OF_AISLE]={1,2,3,4,5,6,7,8,9,10} ;

/*------------------------------------------------------------------------
 Declare Locks, CV and Monitor Variables for Customer/Clerk Interaction
------------------------------------------------------------------------*/


/*Lock to  print multiple PRINT Syscalls*/
int printLock;

/*Lock for mutual exclusion on entering line.*/
int clrkLnLk;
/*Locks for each clerk interaction.*/
int clrkLock[MAX_NO_OF_CLERKS];
/*condition to wait on a line for checkout*/
int clrkLnCV[MAX_NO_OF_CLERKS];
/*condition to interaction btw one customer and clerk at a time.*/
int clrkTkCV[MAX_NO_OF_CLERKS];
/*Condition Variable for Clerks waiting on a break*/
int clOBrkCV;
/* maintains the length of each line.*/
int ckLL[MAX_NO_OF_CASHREGISTERS];
/*maintains the status of each clerk.*/
int ckSt[MAX_NO_OF_CLERKS];


/*all to be used for checkout process. To be used under clrkLock[i].*/
int itemId[MAX_NO_OF_CLERKS];
/*to notify that the last item will be processed for each customer/clerk/cash register transaction.*/
int lstIt[MAX_NO_OF_CLERKS];
/*to store the value of customer id so that clerk and cash register know who they are serving.*/
int cuId[MAX_NO_OF_CLERKS];
/*to store the total bill amount for each customer/clerk/cash register transaction.*/
int rcBA[MAX_NO_OF_CLERKS];
/* will help each clerk and cash register know if the customer wants to buy or return items.*/
int isCuR[MAX_NO_OF_CLERKS];/*to check if it is buying or returning customer*/
/*to be used with clrkLnLk*/
int sgCk; /* To store Id of clerk who call manager when line is above 4*/




/*------------------------------------------------------------------------
 Declare Locks, CV and Monitor Variables for Clerk/CashRegister Interaction
------------------------------------------------------------------------*/

/*All these variables would be modified with the clrkLock[i]
differnet states a cash register can be in.*/

/*for clerk and cash reg interaction.*/
int cshRgsCV[MAX_NO_OF_CASHREGISTERS];
/*status array for all cash registers.*/
int cRSt[MAX_NO_OF_CASHREGISTERS];


/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Manager/Cash Register Interaction
//------------------------------------------------------------------------------*/

/*Lock for Mutual Exclusion between Manager and Cash Register*/
/*No CV's or Monitor variables between them*/
int mCshRgLk[MAX_NO_OF_CASHREGISTERS];
int amNd[MAX_NO_OF_CASHREGISTERS];/*amount needed by cash register to give to customer returning items*/
int cshRgWCV[MAX_NO_OF_CASHREGISTERS];

/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Customer/StockBoy/Aisle Interaction
//------------------------------------------------------------------------------*/


/* lock to check the status of stock boy maintianing the mutual exclusion.*/
int sbStatLk;
/* sbSt for maintianing the status of each stockboy	-1=free		-2=on break	>=0 In store doing stuff*/
int sbSt[MAX_NO_SB];
/*maintain the length of the customer needing help in each aisle.*/
int aHlpLL[MAX_NO_OF_AISLE];
/* to wait on the aisle for help.*/
int aslHlpCV[MAX_NO_OF_AISLE];

/*access stock room one at time.*/
int stckRmLk;
int stRS;/*to be used with stckRmLk*/
int stckRmCV;

/*access the restock list maintianing the data consistency.*/
int reStckLk;

/*lock for updating the Qty on each aisle.*/
int aslQtyLk[MAX_NO_OF_AISLE];
/*maintain the Qty on each aisle*/
int aslQty[MAX_NO_OF_AISLE];
/*if the Qty on aisle is empty, wait till restocked.*/
int aslEmtCV[MAX_NO_OF_AISLE];


/* Lock for the button when no SB available.*/
int noSBBtLk;
/* customers wait till SB is woken by manager.*/
int noSBBtCV;
/*stock boys wait till SB is woken by manager.*/
int sbOBrkCV;
/* maintains the length of the line waiting on the pushbutton.*/
int noSBLL;

/* lock on each SB.*/
int sbLock[MAX_NO_SB];
/* wait on SB.*/
int sbCV[MAX_NO_SB];
/*For the Customer and SB ping pong.*/
int sbCustQA[MAX_NO_SB][2];


/*------------------------------------------------------------------------------
// Declare Locks, CV and Monitor Variables for Manager Interaction
//------------------------------------------------------------------------------*/

/*Customer waiting when all clerks are on break*/
int custWtCV;/* This CV should be used with clrkLnLk*/
int alCkOB;
/*Clerks signaling manager to wakeup another clerk if their linelength>4*/
int wkUpCk;/* This CV should be used with clrkLnLk*/
/*variable modified by customers and read by manager to make note of end of simulation*/
int tlCuEx;








/*=================Cash Register Thread Starts here=========//*/

void CashReg()
{
	int threadId;
	int counter;
	Acquire(cRgTCtrLk);
	threadId=GetMV(cRTCtr);
	Print2("CashReg counter is %d: so thread id is %d.\n",GetMV(cRTCtr),threadId);
	SetMV(cRTCtr, (GetMV(cRTCtr)+1));
	Release(cRgTCtrLk);
	while(1)
	{

		/*========Clerk Cash Register Checkout code below=======*/
		Acquire(clrkLock[threadId]);
		if(GetMV(cRSt[threadId]) == CLERK_WAITING){
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Wait(cshRgsCV[threadId],clrkLock[threadId]);
		}
		else{
			SetMV(cRSt[threadId], CASHREG_WAITING);
			Wait(cshRgsCV[threadId],clrkLock[threadId]);
		}
	counter=1;
		/*cashRegister is interacting with the clerk for customer billing*/

		while(!GetMV(lstIt[threadId]))
		{
			Acquire(printLock);
			Print3("Cash Register [%d] got item [%d] from Clerk [%d] to scan.\n",threadId,counter,threadId);
			Release(printLock);
			counter++;
			SetMV(crCuA[threadId], GetMV(crCuA[threadId]) + itemsInStoreInfo[GetMV(itemId[threadId])]);
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Wait(cshRgsCV[threadId],clrkLock[threadId]);
		}

		Acquire(printLock);
		Print3("Cash Register [%d] got item [%d] from Clerk [%d] to scan.\n",threadId,counter,threadId);
		Release(printLock);
		/*Add the last item to the total of the customer total amount*/
		SetMV(crCuA[threadId], GetMV(crCuA[threadId]) + itemsInStoreInfo[GetMV(itemId[threadId])]);
		Acquire(printLock);
		Print3("Cash Register [%d] has scanned all [%d] items for Customer [%d].\n",threadId,counter, GetMV(cuId[threadId]));
		Release(printLock);

		if(GetMV(isCuR[threadId])){/*customer returning items*/
			/*=========get money from Manager*/
			Acquire(mCshRgLk[threadId]);
			SetMV(crTSl[threadId], GetMV(crTSl[threadId]) - GetMV(crCuA[threadId]));
			SetMV(amNd[threadId], (GetMV(crCuA[threadId])));
			Acquire(printLock);
			Print2("Cash Register [%d] informed the manager that he needs $[%d].\n",threadId, GetMV(amNd[threadId]));
			Release(printLock);
			Wait(cshRgWCV[threadId],mCshRgLk[threadId]);/*waiting for money from manager*/
			Acquire(printLock);
			Print2("Cash Register [%d] received $[%d] from the manager.\n", threadId, GetMV(amNd[threadId]));
			Release(printLock);
			/*========Received cash from Manager*/
			SetMV(amNd[threadId], 0);
			Release(mCshRgLk[threadId]);
			/*Clerk would be giving the final amount to the customer from this rcBA*/
			Acquire(printLock);
			Print3("Cash Register [%d] gave total cost $[%d] for Customer [%d]",threadId, GetMV(crCuA[threadId]),GetMV(cuId[threadId]));
			Print2(" to Clerk [%d] for [%d] items.\n",threadId,counter);
			Release(printLock);
			Acquire(printLock);
			Print2("Cash Register [%d] gave the money to Clerk [%d].\n",threadId,threadId);
			Release(printLock);
			SetMV(rcBA[threadId], GetMV(crCuA[threadId]));
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Wait(cshRgsCV[threadId],clrkLock[threadId]);

			/*Give the receipt*/
			SetMV(crCuA[threadId], 0);
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Acquire(printLock);
			Print3("Cash Register [%d] gave receipt to Clerk [%d] for Customer [%d].\n",threadId,threadId, GetMV(cuId[threadId]));
			Release(printLock);
			SetMV(cRSt[threadId], NOT_SET);

		}
		else{/*customer buying items
			//Clerk would be getting the final amount for the customer from this rcBA*/
			Acquire(printLock);
			Print3("Cash Register [%d] gave total cost $[%d] for Customer [%d]",threadId, GetMV(crCuA[threadId]), GetMV(cuId[threadId]));
			Print2("to Clerk [%d] for [%d] items.\n",threadId,counter);
			Release(printLock);

			SetMV(rcBA[threadId], GetMV(crCuA[threadId]));
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Wait(cshRgsCV[threadId],clrkLock[threadId]);

			/*If I'm here I've got the payment*/
			Acquire(mCshRgLk[threadId]);
			SetMV(crTtl[threadId], GetMV(crTtl[threadId]) + GetMV(crCuA[threadId]));
			SetMV(crTSl[threadId], GetMV(crTSl[threadId]) + GetMV(crCuA[threadId]));
			Release(mCshRgLk[threadId]);

			SetMV(crCuA[threadId], 0);
			Acquire(printLock);
			Print2("Cash Register [%d] got the money from Clerk [%d].\n",threadId,threadId);
			Release(printLock);
			Signal(cshRgsCV[threadId],clrkLock[threadId]);
			Acquire(printLock);
			Print3("Cash Register [%d] gave receipt to Clerk [%d] for Customer [%d].\n",threadId,threadId, GetMV(cuId[threadId]));
			Release(printLock);
			SetMV(cRSt[threadId], NOT_SET);
	}

		/*print the total sales till this point*/
		Acquire(mCshRgLk[threadId]);
		Acquire(printLock);
		Print2("Cash Register [%d] total sale is $[%d].\n",threadId, GetMV(crTSl[threadId]));
		Release(printLock);
		Release(mCshRgLk[threadId]);

		Release(clrkLock[threadId]);

	}

}

/*===================Cash Register thread code ends here==============//////



/*switch case for Sprintf */
void SwitchSprintf(int id, int index)
{

	switch(id){
		case 0:
		{
			name[index] = '0';
			break;
		}
		case 1:
		{
			name[index] = '1';
			break;
		}
		case 2:
		{
			name[index] = '2';
			break;
		}
		case 3:
		{
			name[index] = '3';
			break;
		}
		case 4:
		{
			name[index] = '4';
			break;
		}
		case 5:
		{
			name[index] = '5';
			break;
		}
		case 6:
		{
			name[index] = '6';
			break;
		}
		case 7:
		{
			name[index] = '7';
			break;
		}
		case 8:
		{
			name[index] = '8';
			break;
		}
		case 9:
		{
			name[index] = '9';
			break;
		}
		default:
			Print("Error: Sprintf incorrect id\n");
	}/*end of switch case*/

}

/* Sprintf code */
int Sprintf(char *idName, int id, int len)
{
	int i;
	int id1, id2;
	for(i=0; i<len; i++)
	{
		name[i] = idName[i];
	}
	/*Warning: maximum number for id is 99 */
	if(id<=9){
		SwitchSprintf(id, i);
		i++;
		name[i]='\0';
	}
	else if((id>=10) && (id<=99)){
		id1 = id/10;
		SwitchSprintf(id1, i);
		i++;
		id2 = id%10;
		SwitchSprintf(id2, i);
		i++;
		name[i]='\0';
	}
	else{
		Print("Error: Sprintf incorrect id. greater than 99");
		return -1;
	}
	return i;
}
/*
//============================================
//  Functions for the repeatable test cases
//============================================


//This part of the code generates the common test parameters needed for the simulation to run
// i.e. Condition Variables , Monitors & Locks*/
void generateCmnTestParams()
{
	int i=0,j=0;
	int len;



	Acquire(printLock);
	Print1("\n\nNumber of Clerks = [%d]\n",noOfClerks);
	Print1("Number of StockBoys = [%d]\n",noOfStockBoys);
	Print1("Number of CashRegisters = [%d]\n",noOfCashReg);
	Print1("Number of Customers = [%d]\n",noOfCustomers);
	Print("Number of Managers = [1]\n");
	Print("Number of Aisle = [10]\n");
    Print("Items:\n");
    Print("\tNumber - Price\n");

	for(j=0;j<MAX_NO_OF_AISLE;j++)
	{
		Print2("\t%d  -  %d  \n",j,itemsInStoreInfo[j]);
	}
    Print("\n\n");
	Release(printLock);


	clrkLnLk= CreateLock("clrkLnLk",8);
	sbStatLk = CreateLock("sbStatLk",8);
	stckRmLk = CreateLock("stckRmLk",8);
	stckRmCV = CreateCondition("stckRmCV",8);
	reStckLk = CreateLock("reStckLk",8);
	custWtCV = CreateCondition("custWtCV",8);
	for(i=0;i<MAX_NO_OF_AISLE;i++)
	{
		len = Sprintf("aslQtyLk",i,8);
		aslQtyLk[i]=CreateLock(name , len);

		len = Sprintf("aslEmtCV",i,8);
		aslEmtCV[i]=CreateCondition(name, len);

		len = Sprintf("aslHlpCV",i,8);
		aslHlpCV[i]=CreateCondition(name, len);
	}

	noSBBtLk =CreateLock("noSBBtLk",8);
	noSBBtCV = CreateCondition("noSBBtCV",8);

	sbOBrkCV = CreateCondition("sbOBrkCV",8);
	for(i=0;i<noOfStockBoys;i++)
	{
		len = Sprintf("sbLock",i,6);
		sbLock[i]=CreateLock(name, len);

		len = Sprintf("sbCV",i,4);
		sbCV[i]=CreateCondition(name, len);

	}
	for(i=0;i<noOfClerks;i++)
	{
		len = Sprintf("clrkLock",i,8);
		clrkLock[i]=CreateLock(name, len);

		len = Sprintf("clrkLnCV",i,8);
		clrkLnCV[i]=CreateCondition(name, len);

		len = Sprintf("clrkTkCV",i,8);
		clrkTkCV[i]=CreateCondition(name, len);
	}

	for(i=0;i<noOfCashReg;i++)
	{
		len = Sprintf("cshRgsCV",i,8);
		cshRgsCV[i]=CreateCondition(name, len);

		len = Sprintf("mCshRgLk",i,8);
		mCshRgLk[i]=CreateLock(name, len);

		len = Sprintf("cshRgWCV",i,8);
		cshRgWCV[i]=CreateCondition(name, len);
	}



	clOBrkCV=CreateCondition("clOBrkCV",8);

	for(i=0;i<noOfCustomers;i++)
	{
		customerData[i].noOfItemsToBePurchased=(int)((GetRand()%10)+1);
		for(j=0;j<customerData[i].noOfItemsToBePurchased;j++)
		{
			customerData[i].itemsToBePurchased[j]=(int)(GetRand()%10);
		}
		if(GetRand()%5==4){
			customerData[i].customerReturning=TRUE;
		}
		else{
			customerData[i].customerReturning=FALSE;
		}
	}



	/* Create Monitor variables*/
	cuTCtr = CreateMV("cuTCtr",6,0);
	cRTCtr = CreateMV("cRTCtr",6,0);
	ckTCtr = CreateMV("ckTCtr",6,0);
	sbTCtr = CreateMV("sbTCtr",6,0);

	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("ckLL",i,4);
		ckLL[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("ckSt",i,4);
		ckSt[i] = CreateMV(name, len, CLERK_FREE);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("itId",i,4);
		itemId[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("lstIt",i,5);
		lstIt[i] = CreateMV(name, len, FALSE);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("cuId",i,4);
		cuId[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("rcBA",i,4);
		rcBA[i] = CreateMV(name, len, -1);
	}

	for(i=0; i<noOfClerks; i++){
		len = Sprintf("isCuR",i,5);
		isCuR[i] = CreateMV(name, len, FALSE);
	}

	sgCk = CreateMV("sgCk", 4, -1);


	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("crCuA",i,5);
		crCuA[i] = CreateMV(name, len, 0);

		len = Sprintf("crTtl",i,5);
		crTtl[i] = CreateMV(name, len, 0);

		len = Sprintf("crTSl",i,5);
		crTSl[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfCashReg; i++){
		len = Sprintf("cRSt",i,4);
		cRSt[i] = CreateMV(name, len, NOT_SET);

		len = Sprintf("amNd",i,4);
		amNd[i] = CreateMV(name, len, 0);
	}


	noSBLL = CreateMV("noSBLL",6,0);

	tlCuEx = CreateMV("tlCuEx",6,0);

	wkUpCk = CreateMV("wkUpCk",6,FALSE);

	alCkOB = CreateMV("alCkOB",6,FALSE);

	stRS = CreateMV("stRS",4,-1);


	for(i=0; i<MAX_NO_OF_AISLE; i++){
		len = Sprintf("aslQy",i,5);
		aslQty[i] = CreateMV(name, len, 3);

		len = Sprintf("aHlpL",i,5);
		aHlpLL[i] = CreateMV(name, len, 0);
	}

	for(i=0; i<noOfStockBoys; i++){
		len = Sprintf("sbSt",i,4);
		sbSt[i] = CreateMV(name, len, -1);
	}


	for(i=0; i<noOfStockBoys; i++){
		len = Sprintf("sbC0Q",i,5);
		sbCustQA[i][0] = CreateMV(name, len, 0);

		len = Sprintf("sbC1Q",i,5);
		sbCustQA[i][1] = CreateMV(name, len, 0);
	}


}

/*This test case runs the entire simulation with minimum requirements*/
void testcase12()
{


	noOfClerks=3;
	noOfCashReg=3;
	noOfCustomers=20;
	noOfStockBoys=3;

	generateCmnTestParams();
}




/*====================================================================================
// Function Problem2() which is called for simulation of the Part 2 of Assignment 1
// Contains the Test Case Selection Menu in it.
//====================================================================================*/


void main()
{


	printLock = CreateLock("printLock",9);
	cusTCtrLk=CreateLock("cusTCtrLk",9);
	cRgTCtrLk=CreateLock("cRgTCtrLk",9);
	clkTCtrLk=CreateLock("clkTCtrLk",9);
	sbTCtrLk=CreateLock("sbTCtrLk",8);

	/* Call testcase with minimum requiements */
	testcase12();

	CashReg();
}
